/*
	File:				ObjectManager.cpp
	Course:		SGP 1208
	Author:		Kori Po
	Purpose:	Manages all in game objects
*/

#include "ObjectManager.h"
#include "IEntity.h"
#include "Entity.h"
#include "Layer.h"
#include <cassert>
#include <algorithm>

/*static*/ CObjectManager* CObjectManager::s_Instance = nullptr;


bool MySort( IEntity* ptrA,  IEntity* ptrB )
{
	//int tempA = ptrA->GetX();
	//int tempA = ptrA->GetX();

	return ptrA->GetY() < ptrB->GetY();
}


/*
bool MySort( int nX, int nY  )
{
	return nX > nY;
}
*/

CObjectManager::CObjectManager(void)
{
	m_bPlayersIterating = false;
	m_bEnemiesIterating = false;
	m_bItemsIterating = false;
	m_bAbilitiesIterating = false;
	m_bLayersIterating = false;
	m_bMegaIterating = false;
}

CObjectManager::~CObjectManager(void)
{
	/* Do Nothing */
}

/*static*/ CObjectManager* CObjectManager::GetInstance( void )
{
	// Dynamically allocate
	if( s_Instance == nullptr )
		s_Instance = new CObjectManager;

	return s_Instance;
}

/*static*/ void CObjectManager::DeleteInstance( void )
{
	// Deallocate
	delete s_Instance;
	s_Instance = nullptr;
}

void CObjectManager::AddObject( IEntity* ptr )
{
	// Iteration check
	assert( m_bPlayersIterating == false && "CANNOT call AddObject while players iterating!" );
	assert( m_bEnemiesIterating == false && "CANNOT call AddObject while enemies iterating!" );
	assert( m_bItemsIterating == false && "CANNOT call AddObject while items iterating!" );
	assert( m_bAbilitiesIterating == false && "CANNOT call AddObject while abilities are iterating!" );
	assert( m_bLayersIterating == false && "CANNOT call AddObject while layers are iterating!" );
	assert( m_bMegaIterating == false && "CANNOT call AddObject while MEGA iterating!" );

	// Null Ptr Check
	assert( ptr != nullptr && "YOU ARE A JERK!" );

	CEntity* pOmega = dynamic_cast< CEntity* >( (ptr) );

	if(pOmega->GetType()  == OBJ_PLAYER)
	{
		m_vPlayers.push_back( ptr );
		m_vMega.push_back( ptr );
	}
	else if(pOmega->GetType()  == OBJ_ENEMY)
	{
		m_vEnemies.push_back( ptr );
		m_vMega.push_back( ptr );
	}
	else if(pOmega->GetType()  == OBJ_ITEM)
	{
		m_vItems.push_back( ptr );
		m_vMega.push_back( ptr );
	}
	else if(pOmega->GetType()  == OBJ_ABILITY)
	{
		m_vAbilities.push_back( ptr );
		m_vMega.push_back( ptr );
	}
	else if(pOmega->GetType()  == OBJ_LAYER)
	{
		m_vLayers.push_back( ptr );
		m_vMega.push_back( ptr );
	}
	else
	{
		assert( "We don't have those please call tech support or double check our dictionaries" );
	}

	// Add a reference!
	ptr->AddRef();
}

void CObjectManager::RemoveObject( IEntity* ptr )
{
	// Iteration check
	assert( m_bPlayersIterating == false && "CANNOT call RemoveObject while players iterating!" );
	assert( m_bEnemiesIterating == false && "CANNOT call RemoveObject while enemies iterating!" );
	assert( m_bItemsIterating == false && "CANNOT call RemoveObject while items iterating!" );
	assert( m_bAbilitiesIterating == false && "CANNOT call RemoveObject while abilities are iterating!" );
	assert( m_bLayersIterating == false && "CANNOT call RemoveObject while layers are iterating!" );
	assert( m_bMegaIterating == false && "CANNOT call RemoveObject while MEGA iterating!" );

	// Null Ptr Check
	assert( ptr != nullptr && "...REALLY BRO?" );

	// Wheres the ptr?
	for( PListIterator iter = m_vPlayers.begin(); iter != m_vPlayers.end(); ++iter )
	{
		if( (*iter) == ptr )
		{
			// Release the reference!
			(*iter)->Release();

			// Remove from the container
			iter = m_vPlayers.erase( iter );
			break;
		}
	}

	// Is it here?
	for( EListIterator iter = m_vEnemies.begin(); iter != m_vEnemies.end(); ++iter )
	{
		if( (*iter) == ptr )
		{
			// Release the reference!
			(*iter)->Release();

			// Remove from the container
			iter = m_vEnemies.erase( iter );
			break;
		}
	}

	// How about here?
	for( IListIterator iter = m_vItems.begin(); iter != m_vItems.end(); ++iter )
	{
		if( (*iter) == ptr )
		{
			// Release the reference!
			(*iter)->Release();

			// Remove from the container
			iter = m_vItems.erase( iter );
			break;
		}
	}

	// May be here?
	for( AListIterator iter = m_vAbilities.begin(); iter != m_vAbilities.end(); ++iter )
	{
		if( (*iter) == ptr )
		{
			// Release the reference!
			(*iter)->Release();

			// Remove from the container
			iter = m_vAbilities.erase( iter );
			break;
		}
	}

	// It has to be here......
	for( LListIterator iter = m_vLayers.begin(); iter != m_vLayers.end(); ++iter )
	{
		if( (*iter) == ptr )
		{
			// Release the reference!
			(*iter)->Release();

			// Remove from the container
			iter = m_vLayers.erase( iter );
			break;
		}
	}

	// Mega Erase
	for( MListIterator iter = m_vMega.begin(); iter != m_vMega.end(); ++iter )
	{
		if( (*iter) == ptr )
		{
			// Release the reference!
			(*iter)->Release();

			// Remove from the container
			iter = m_vMega.erase( iter );
			break;
		}
	}

	assert( ptr != nullptr && "...REALLY BRO?" );

}

void CObjectManager::RemoveAllObjects( void )
{
	// Iteration check
	assert( m_bPlayersIterating == false && "CANNOT call RemoveAllObjects while players are iterating!" );
	assert( m_bEnemiesIterating == false && "CANNOT call RemoveAllObjects while enemies are iterating!" );
	assert( m_bItemsIterating == false && "CANNOT call RemoveAllObjects while items are iterating!" );
	assert( m_bAbilitiesIterating == false && "CANNOT call RemoveAllObjects while abilities are iterating!" );
	assert( m_bLayersIterating == false && "CANNOT call RemoveAllObjects while layers are iterating!" );
	assert( m_bMegaIterating == false && "CANNOT call RemoveAllObjects while MEGA iterating!" );

	// Release all Players
	for( PListIterator iter = m_vPlayers.begin(); iter != m_vPlayers.end(); ++iter )
	{
		(*iter)->Release();
	}

	// Release all Enemies
	for( EListIterator iter = m_vEnemies.begin(); iter != m_vEnemies.end(); ++iter )
	{
		(*iter)->Release();
	}

	// Release all Items
	for( IListIterator iter = m_vItems.begin(); iter != m_vItems.end(); ++iter )
	{
		(*iter)->Release();
	}

	// Release all Abilities
	for( AListIterator iter = m_vAbilities.begin(); iter != m_vAbilities.end(); ++iter )
	{
		(*iter)->Release();
	}

	// Release all Layer
	for( LListIterator iter = m_vLayers.begin(); iter != m_vLayers.end(); ++iter )
	{
		(*iter)->Release();
	}

	// Release all Mega
	for( MListIterator iter = m_vMega.begin(); iter != m_vMega.end(); ++iter )
	{
		(*iter)->Release();
	}

	// Collapse the all vectors
	m_vPlayers.clear();
	m_vEnemies.clear();
	m_vItems.clear();
	m_vAbilities.clear();
	m_vLayers.clear();
	m_vMega.clear();
}

void CObjectManager::UpdateAllObjects( float fElapsedTime )
{
	// Iteration check
	assert( m_bPlayersIterating == false && "CANNOT call UpdateAllObjects while players are iterating!" );
	assert( m_bEnemiesIterating == false && "CANNOT call UpdateAllObjects while enemies are iterating!" );
	assert( m_bItemsIterating == false && "CANNOT call UpdateAllObjects while items are iterating!" );
	assert( m_bAbilitiesIterating == false && "CANNOT call UpdateAllObjects while abilities are iterating!" );
	assert( m_bLayersIterating == false && "CANNOT call UpdateAllObjects while layers are iterating!" );
	assert( m_bMegaIterating == false && "CANNOT call UpdateAllObjects while MEGA iterating!" );

	// We are now iterating!
	m_bPlayersIterating = true;
	m_bEnemiesIterating = true;
	m_bItemsIterating = true;
	m_bAbilitiesIterating = true;
	m_bLayersIterating = true;
	m_bMegaIterating = true;

	// Update all Players
	for( PListIterator iter = m_vPlayers.begin(); iter != m_vPlayers.end(); ++iter )
	{
		(*iter)->Update( fElapsedTime );
	}

	// Update all Enemies
	for( EListIterator iter = m_vEnemies.begin(); iter != m_vEnemies.end(); ++iter )
	{
		(*iter)->Update( fElapsedTime );
	}

	// Update all Items
	for( IListIterator iter = m_vItems.begin(); iter != m_vItems.end(); ++iter )
	{
		(*iter)->Update( fElapsedTime );
	}

	// Update all Abilities
	for( AListIterator iter = m_vAbilities.begin(); iter != m_vAbilities.end(); ++iter )
	{
		(*iter)->Update( fElapsedTime );
	}

	// Update all Layer
	for( LListIterator iter = m_vLayers.begin(); iter != m_vLayers.end(); ++iter )
	{
		(*iter)->Update( fElapsedTime );
	}

	/////////////////////////
	//for( PListIterator IT_P_X = m_vPlayers.begin(); IT_P_X != m_vPlayers.end(); ++IT_P_X )
	//{
	//	 IEntity* pZeta = dynamic_cast< IEntity* >( (*IT_P_X) );
	//	 m_vMega.push_back(pZeta);
	//}
	//
	//for( EListIterator IT_E_Y = m_vEnemies.begin(); IT_E_Y != m_vEnemies.end(); ++IT_E_Y )
	//{
	//	  IEntity* pZeta = dynamic_cast< IEntity* >( (*IT_E_Y) );
	//	 m_vMega.push_back(pZeta);
	//}
	//
	//for( AListIterator IT_A_Y = m_vAbilities.begin(); IT_A_Y != m_vAbilities.end(); ++IT_A_Y )
	//{
	//	IEntity* pZeta = dynamic_cast< IEntity* >( (*IT_A_Y) );
	//	 m_vMega.push_back(pZeta);
	//}

	std::sort(m_vMega.begin(), m_vMega.end(), MySort);
	//////////////////////////////////////////////

	// Done
	m_bPlayersIterating = false;
	m_bEnemiesIterating = false;
	m_bItemsIterating = false;
	m_bAbilitiesIterating = false;
	m_bLayersIterating = false;
	m_bMegaIterating = false;
}

void CObjectManager::CheckCollisions( void )
{
	// Iteration check
	assert( m_bPlayersIterating == false && "CANNOT call CheckCollisions while players iterating!" );
	assert( m_bEnemiesIterating == false && "CANNOT call CheckCollisions while enemies iterating!" );
	assert( m_bItemsIterating == false && "CANNOT call CheckCollisions while items iterating!" );
	assert( m_bAbilitiesIterating == false && "CANNOT call CheckCollisions while abilities are iterating!" );
	assert( m_bLayersIterating == false && "CANNOT call CheckCollisions while layers are iterating!" );
	assert( m_bMegaIterating == false && "CANNOT call CheckCollisions while MEGA iterating!" );

	// We are now iterating!
	m_bPlayersIterating = true;
	m_bEnemiesIterating = true;
	m_bItemsIterating = true;
	m_bAbilitiesIterating = true;
	m_bLayersIterating = true;
	m_bMegaIterating = true;

	///////////////////////////////////////////
	// Iterating
	/////////////////////////////////////////

	/*
	Naming Covention
	IT = Iterator
	
	P	= Player
	E	= Enemies
	I	= Items
	A = Abilites
	L	= Layer
	
	X = First List
	Y = To Compare
	*/

	///////////////////////
	// Players X Layer
	////////////////////////

	// Iterate Players
	for( PListIterator IT_P_X = m_vPlayers.begin(); IT_P_X != m_vPlayers.end(); ++IT_P_X )
	{
		// Iterate Layer
		for( LListIterator IT_L_Y = m_vLayers.begin(); IT_L_Y != m_vLayers.end(); ++IT_L_Y )
		{
			// Check Collision
			//The Layer's Check Collision is called against the player
			if( (*IT_L_Y)->CheckCollision( *IT_P_X ) )
				break;
		}
	}

	///////////////////////
	// Players X Enemies
	////////////////////////

	// Iterate Players
	for( PListIterator IT_P_X = m_vPlayers.begin(); IT_P_X != m_vPlayers.end(); ++IT_P_X )
	{
		// Iterate Enemies
		for( EListIterator IT_E_Y = m_vEnemies.begin(); IT_E_Y != m_vEnemies.end(); ++IT_E_Y )
		{
			// Check Collision
			if( (*IT_P_X)->CheckCollision( *IT_E_Y ) )
				break;
		}
	}

	///////////////////////
	// Players X Items
	////////////////////////

	// Iterate Players
	for( PListIterator IT_P_X = m_vPlayers.begin(); IT_P_X != m_vPlayers.end(); ++IT_P_X )
	{
		// Iterate Items
		for( EListIterator IT_I_Y = m_vItems.begin(); IT_I_Y != m_vItems.end(); ++IT_I_Y )
		{
			// Check Collision
			if( (*IT_P_X)->CheckCollision( *IT_I_Y ) )
				break;
		}
	}
	
	   ///////////////////////
	 // Players X Abilities
	////////////////////////

	// Iterate Players
	for( PListIterator IT_P_X = m_vPlayers.begin(); IT_P_X != m_vPlayers.end(); ++IT_P_X )
	{
		// Iterate Abilities
		for( EListIterator IT_A_Y = m_vAbilities.begin(); IT_A_Y != m_vAbilities.end(); ++IT_A_Y )
		{
			// Check Collision
			if( (*IT_P_X)->CheckCollision( *IT_A_Y ) )
				break;
		}
	}

	  ////////////////////////
     // Enemies X Abilities
	///////////////////////

	// Iterate Enemies
	for( PListIterator IT_E_X = m_vPlayers.begin(); IT_E_X != m_vPlayers.end(); ++IT_E_X )
	{
		// Iterate Abiliities
		for( EListIterator IT_A_Y = m_vAbilities.begin(); IT_A_Y != m_vAbilities.end(); ++IT_A_Y )
		{
			// Check Collision
			if( (*IT_E_X)->CheckCollision( *IT_A_Y ) )
				break;
		}
	}

	  ///////////////////////
	 // Enemies X Layer
	//////////////////////

	// Iterate Enemies
	for( PListIterator IT_E_X = m_vPlayers.begin(); IT_E_X != m_vPlayers.end(); ++IT_E_X )
	{
		// Iterate Layer
		for( LListIterator IT_L_Y = m_vLayers.begin(); IT_L_Y != m_vLayers.end(); ++IT_L_Y )
		{
			// Check Collision
			if( (*IT_E_X)->CheckCollision( *IT_L_Y ) )
				break;
		}
	}

	// Done
	m_bPlayersIterating = false;
	m_bEnemiesIterating = false;
	m_bItemsIterating = false;
	m_bAbilitiesIterating = false;
	m_bLayersIterating = false;
	m_bMegaIterating = false;
}

void CObjectManager::RenderAllObjects( void )
{
	  /////////////////////////////////////////
	// Iteration check
  /////////////////////////////////////////
	assert( m_bPlayersIterating == false && "CANNOT call RenderAllObjects while players iterating!" );
	assert( m_bEnemiesIterating == false && "CANNOT call RenderAllObjects while enemies iterating!" );
	assert( m_bItemsIterating == false && "CANNOT call RenderAllObjects while items iterating!" );
	assert( m_bAbilitiesIterating == false && "CANNOT call RenderAllObjects while abilities are iterating!" );
	assert( m_bLayersIterating == false && "CANNOT call RenderAllObjects while layers are iterating!" );
	assert( m_bMegaIterating == false && "CANNOT call RenderAllObjects while MEGA iterating!" );

	  /////////////////////////////////////////
	// We are now iterating!
  /////////////////////////////////////////
	m_bPlayersIterating = true;
	m_bEnemiesIterating = true;
	m_bItemsIterating = true;
	m_bAbilitiesIterating = true;
	m_bLayersIterating = true;
	m_bMegaIterating = true;

	   /////////////////////////////////////////
	 // Iterating
   /////////////////////////////////////////
	/*
	Naming Covention
	IT = Iterator
	
	P = Player
	E = Enemies
	I = Items
	A = Abilites
	L = Layers
	M = Mega

	X = First List
	Y = To Compare
	*/
	/////////////////////////////

	  ////////////////////////////////////////////////////////////
	 // Form and combine froces to create MEGA LIST!!!
	//////////////////////////////////////////////////////////


	/*
	for( AListIterator IT_A_Y = m_vAbilities.begin(); IT_A_Y != m_vAbilities.end(); ++IT_A_Y )
	 {
		 IEntity* pZeta = dynamic_cast< IEntity* >( (*IT_A_Y) );
		 m_vMega.push_back(pZeta);
	 }
	
	 for( IListIterator IT_I_X = m_vItems.begin(); IT_I_X != m_vItems.end(); ++IT_I_X )
	 {
		  IEntity* pZeta = dynamic_cast< IEntity* >( (*IT_I_X) );
		 m_vMega.push_back(pZeta);
	 }
	 */

	  //////////////////////
	 // Sort Mega List 
	/////////////////////




	 
	/*
	  for( MListIterator IT_M_X = m_vMega.begin(); IT_M_X != m_vMega.end(); ++IT_M_X )
	 {
		for( MListIterator IT_M_Y = m_vMega.begin(); IT_M_Y != m_vMega.end(); ++IT_M_Y )
		{

			// D-Cast IEntity to CEntity
			CEntity* pAlpha = dynamic_cast< CEntity* >( (*IT_M_X) );
			CEntity* pBeta = dynamic_cast< CEntity* >( (*IT_M_Y) );

			IEntity* Temp = (*IT_M_X);
			
			 if( pAlpha->GetY() < pBeta->GetY() )
			 {
				//std::swap( pAlpha, pBeta);
				 std::swap( IT_M_X, IT_M_Y);
				 
				 //Switcharoo((*IT_M_X), (*IT_M_Y));
				// m_vMega.swap(IT_M_X,IT_M_Y);
			 }
		}
	 }
	 */

	   /////////////////////
	 // Render Layers
   /////////////////////

	for( LListIterator iter = m_vLayers.begin(); iter != m_vLayers.end(); ++iter )
	{
		(*iter)->Render();
	}

	  	/////////////////////
	 // Render Mega
   /////////////////////

	for( MListIterator iter = m_vMega.begin(); iter != m_vMega.end(); ++iter )
	{
		(*iter)->Render();
	}
	
	//m_vMega.clear();

	// Done
	m_bPlayersIterating = false;
	m_bEnemiesIterating = false;
	m_bItemsIterating = false;
	m_bAbilitiesIterating = false;
	m_bLayersIterating = false;
	m_bMegaIterating = false;
}

void CObjectManager::Switcharoo( IEntity* ptrA,  IEntity* ptrB )
{
	IEntity* Temp;
	Temp = ptrA;
	ptrA = ptrB;
	ptrB = Temp;
}

